/////////////////////////////////////////////////////////////////////////
//
// Amuse Engine SDK - visibility
// Copyright( c) 2015.  All Rights Reserved
//
// File:		AEVisibilityData.h
// Author:		Gianluca Belardelli
// Date:		19/06/2015
//
/////////////////////////////////////////////////////////////////////////
#ifndef _AEVISIBILITYDATA_H_
#define _AEVISIBILITYDATA_H_

// flags for visibility testing
#define AELODTEST_NONE                 0
#define AELODTEST_CLIPPOSITION         32
#define AELODTEST_BOUNDINGBOX          64
#define AELODTEST_UNUSED               128

#define AEVIS_HAS_NO_MODEL                  1
#define AEVIS_EXCLUDED_FROM_VISTEST         2
#define AEVIS_IS_INACTIVE                   4
#define AEVIS_PERFORM_LODTEST               ( AELODTEST_CLIPPOSITION | AELODTEST_BOUNDINGBOX )
#define AEVIS_EXCLUDED_FROM_OCCLUSIONQUERY  16
#define AEVIS_NO_VISZONE_ASSIGNMENT         256

/// \brief
///   Class that houses members and functions for visiblity clipping: bounding box and near/far clip distances.
///
/// This class is used as common base class for VisBaseEntity_cl or VisStaticGeometryInstance_cl to add
/// visibility capabilities. The visibility collector uses convenience functions (such as IsClipped) to
/// perform visibility tests in a unified way.
class AEVisibilityData
{
protected:
	AEBoundingBox			m_bbBoundingBox;
	AEUINT32   				m_uiVisibleMask;
	AEFLOAT32 				m_fFarClipDistance;
	AEINT32   				m_nPerformTestFlags;
	AEFLOAT32 				m_fNearClipDistance;
	AEVector4f 				m_vcClipReference;

public:
	/// \brief Constructor
	AE_DLLIMPEXP AEVisibilityData( void );

	/// \brief Destructor
	AE_DLLIMPEXP virtual ~AEVisibilityData( void );

	/// \brief Sets the absolute bounding box (in world space) for this instance.
	AE_FORCEINLINE void SetBoundingBox( const AEBoundingBox &abbBox );

	/// \brief Returns the previously set bounding box.
	AE_FORCEINLINE const AEBoundingBox &GetBoundingBox( void ) const;

	/// \brief Sets the far clip distance (in world space) for this instance. A value of <=0 means no far clipping.
	AE_FORCEINLINE void SetFarClipDistance( AEFLOAT32 fClipDistance );

	/// \brief Returns the previously set far clip distance.
	AE_FORCEINLINE AEFLOAT32 GetFarClipDistance( void ) const;

	/// \brief Sets the near clip distance (in world space) for this instance. A value of <=0 means no near clipping.
	AE_FORCEINLINE void SetNearClipDistance( AEFLOAT32 fClipDistance );

	/// \brief Returns the previously set near clip distance.
	AE_FORCEINLINE AEFLOAT32 GetNearClipDistance( void ) const;

	/// \brief Sets the visibility bitmask for this instance. This
	/// An object will only be rendered into a  render contexts, if the logical AND result of this
	/// bitmask and the context's bitmask (see VisRenderContext_cl::SetRenderFilterMask) is not zero.
	/// By default, all bits are set.
	/// To hide an object completely in all contexts, use SetVisibleBitmask(0)
	/// \param nMask
	///   The new filter bitmask
	AE_DLLIMPEXP virtual void SetVisibleBitmask( AEUINT32 uiMask );

	/// \brief Returns the previously set visibility bitmask for this geometry instance.
	AE_FORCEINLINE AEUINT32 GetVisibleBitmask( void ) const;

	/// \brief Sets the near and far clip distance (in world units) and a clipping reference position to measure the distance against camera
	/// This function does not set the clipping mode. That can be set by SetClipMode after calling this function.
	/// This function sets the clipping mode to either VIS_LOD_TEST_CLIPPOSITION or VIS_LOD_TEST_NONE depending on parameter
	/// pvClipRefPosition being NULL or not.
	/// \param fNear
	///   Near clipping distance in world units. 0 for no near clipping
	/// \param fFar
	///   Far clipping distance in world units. 0 for no far clipping
	/// \param pvClipRefPosition
	///   Clipping reference position. Pass NULL to disable clipping. To enable bounding box clipping, call SetClipMode afterwards
	AE_DLLIMPEXP void SetClipSettings( AEFLOAT32 fNear, AEFLOAT32 fFar, const AEVector4f* pvClipRefPosition );

	/// \brief Defines how near/far clipping is determined on this instance
	/// \param iFlags
	///   Determines the clipping behavior. There are 5 valid combinations:
	///    \li VIS_LOD_TEST_NONE: No clipping is performed
	///    \li VIS_LOD_TEST_CLIPPOSITION: Clipping is performed relative to clip position (passed to SetClipSettings), taking LOD scaling factor into account
	///    \li VIS_LOD_TEST_BOUNDINGBOX: Distance is measured against the bounding biox, taking LOD scaling factor into account
	AE_FORCEINLINE void SetClipMode( AEINT32 nFlags=AELODTEST_CLIPPOSITION );

	/// \brief Returns bit constants of type VIS_LOD_TEST_NONE, VIS_LOD_TEST_CLIPPOSITION and VIS_LOD_TEST_BOUNDINGBOX. Valid combinations are described in SetClipMode
	AE_FORCEINLINE AEINT32 GetClipMode( AEINT32 nMask = AEVIS_PERFORM_LODTEST ) const;

	/// \brief Returns all bit flags relevant for visibility
	AE_FORCEINLINE AEINT32 GetVisibilityTestFlags( void ) const;

	/// \brief Sets all bit flags relevant for visibility
	AE_FORCEINLINE void SetVisibilityTestFlags( AEINT32 nFlags );

	/// \brief All new bit flag(s) to the bitmask that is relevant for visibility
	AE_FORCEINLINE void AddVisibilityTestFlag( AEINT32 nFlags );

	/// \brief Remove bit flag(s) from the bitmask that is relevant for visibility
	AE_FORCEINLINE void RemoveVisibilityTestFlag( AEINT32 nFlags );

	/// \brief Returns the clipping reference position which is used if clipping mode is set to VIS_LOD_TEST_CLIPPOSITION
	AE_FORCEINLINE const AEVector4f& GetClipReference( void ) const;

	/// \brief Helper function for visibility loop implementations
	/// \param vcCameraPos
	///   Camera position to test LOD clipping
	/// \param fLODScaleSqr
	///   Square of the LOD scaling factor
	/// \returns
	///   true if the object is clipped either by near of far clip distance (if specified)
	AE_FORCEINLINE AEBOOL32 IsNearOrFarClipped( const AEVector4f &vcCameraPos, AEFLOAT32 fLODScaleSqr=1.f ) const;

	/// \brief Function that performs near/far clipping check based on the given squared distance to the camera
	/// \param fDistSqr squared distance to the camera
	/// \returns true if the object is clipped either by near of far clip distance (if specified)
	AE_FORCEINLINE AEBOOL32 IsNearOrFarClipped( AEFLOAT32 fDistSqr ) const;

	/// \brief Helper function to retrieve current distance to a given plane
	/// \param vcCameraPos Camera position to test the distance against
	/// \param fLODScaleSqr Square of the LOD scaling factor
	/// \param fPlane Reference plane distance, will be subtracted from final result
	/// \returns Relative distance from object to given plane
	AE_FORCEINLINE AEFLOAT32 GetDistanceToPlane( const AEVector4f &vcCameraPos, AEFLOAT32 fLODScaleSqr=1.f, AEFLOAT32 fPlane=0.f ) const;

	/// \brief Helper function for visibility loop implementations
	/// \param uiFilterMask Context's filter mask to test against the m_uiVisibleMask member
	/// \param vcCameraPos Camera position to test LOD clipping
	/// \param fLODScaleSqr Square of the LOD scaling factor. Pass a value < 0.0 to ignore near or far clipping
	/// \returns true if the object is clipped either by near of far clip distance (if specified)
	AE_FORCEINLINE AEBOOL32 IsClipped( AEUINT32 uiFilterMask, const AEVector4f& vcCameraPos, AEFLOAT32 fLODScaleSqr ) const;

	/// \brief Translate visibility data (bounding box and reference position) by specified offset
	AE_FORCEINLINE void TranslateVisibility( const AEVector4f &vcOffset );

	/// \brief Helper function to merge another visibility info into this one in a way that it is guaranteed that both are always visible
	AE_FORCEINLINE void MergeVisibilityData( const AEVisibilityData &vdOther );
};

#include "AEVisibilityData.inl"

#endif // VISAPIVISIBILITYDATA_HPP_INCLUDED
